using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using FastReflectionLib;

namespace HF.Repository
{
    public class RepositoryReflectionCache
    {
      
        private static readonly ReaderWriterLockSlim mPropertyInfoLock;
        private static readonly Dictionary<Type, PropertyInfo[]> propertyInfoCache;

        private static readonly ReaderWriterLockSlim mFieldInfoLock;
        private static readonly Dictionary<Type, FieldInfo[]> fieldInfoCache;
        
        private static readonly ReaderWriterLockSlim mModelKeyLock;
        private static readonly Dictionary<Type, RepositoryModelAttribute> repositoryModelAttributeCache;



        static RepositoryReflectionCache()
        {
            propertyInfoCache = new Dictionary<Type, PropertyInfo[]>();
            mPropertyInfoLock = new ReaderWriterLockSlim();

            fieldInfoCache = new Dictionary<Type, FieldInfo[]>();
            mFieldInfoLock = new ReaderWriterLockSlim();

            repositoryModelAttributeCache = new Dictionary<Type, RepositoryModelAttribute>();
            mModelKeyLock = new ReaderWriterLockSlim();


        }


        internal static PropertyInfo[] GetPropertyInfos(Type type)
        {
            PropertyInfo[] value = CacheHelper.Get((out PropertyInfo[] p) => propertyInfoCache.TryGetValue(type, out p),
                                                   () => type.GetProperties().OrderBy(p => p.Name).ToArray(),
                                                   p => propertyInfoCache[type] = p,
                                                   mPropertyInfoLock
                );
            return value;
        }

        internal static RepositoryModelAttribute GetRepositoryModelAttribute(Type type)
        {

            RepositoryModelAttribute value =
                CacheHelper.Get(
                    (out RepositoryModelAttribute p) => repositoryModelAttributeCache.TryGetValue(type, out p),
                    () => Attribute.GetCustomAttribute(type, typeof (RepositoryModelAttribute)) as
                          RepositoryModelAttribute,
                    p => repositoryModelAttributeCache[type] = p,
                    mModelKeyLock
                    );
            return value;
        }


        public static string GetTableName(Type type)
        {
            RepositoryModelAttribute value = GetRepositoryModelAttribute(type);
            return value == null ? string.Empty : value.TableName;
        }

        public static string GetKeyName(Type type)
        {
            RepositoryModelAttribute value = GetRepositoryModelAttribute(type);
            return value == null ? string.Empty : value.KeyName;
        }


        internal static object GetKeyValue(Type type, ReadonlyRepositoryModel model)
        {
            PropertyInfo propertyInfo = GetPropertyInfos(type).SingleOrDefault(p => p.Name == GetKeyName(type));
            return propertyInfo.FastGetValue(model);
        }

        internal static PropertyInfo GetPropertyInfo(Type type, string property)
        {
            return GetPropertyInfos(type).FirstOrDefault(p => p.Name == property);
        }
        internal static FieldInfo[] GetFieldInfos(Type type)
        {
            FieldInfo[] value = CacheHelper.Get((out FieldInfo[] p) => fieldInfoCache.TryGetValue(type, out p),
                                                  type.GetFields,
                                                  p => fieldInfoCache[type] = p,
                                                  mFieldInfoLock
               );
            return value;
        }
        internal static FieldInfo GetFieldInfo(Type type, string field)
        {
            return GetFieldInfos(type).FirstOrDefault(p => p.Name == field);
        }

    }
}